#ifndef _EVENT_QUEUE_H_
#define _EVENT_QUEUE_H_

#include <stdint.h>
#include <string.h>

#ifndef NULL
#define NULL 0
#endif

#define EVQ_SIZE_BIT 3
#define EVQ_SIZE (1 << EVQ_SIZE_BIT)

static const uint32_t EVQ_SIZE_MASK = EVQ_SIZE - 1;

struct event {
        uint8_t type;
        void *arg;
};

/* insert at tail, remove at head */
struct event_queue {
        struct event event_buf[EVQ_SIZE];
        volatile uint32_t head;
        volatile uint32_t tail;
};

#define EVENT(p_event_queue, idx) &((p_event_queue)->event_buf[(idx) & EVQ_SIZE_MASK])

/****************************************************************/
/*                           User APIs                          */
/****************************************************************/

/*
 * @brief       init an event queue, simply fill it with 0.
 * @param       pointer to the event queue to be initialized.
 * @retval      none
 */
static inline void event_queue_init(struct event_queue *event_queue) {
        memset(event_queue, 0, sizeof(struct event_queue));
}

/*
 * @brief       post an event to the event queue
 * @param       event_queue: Pointer to an event queue
 * @param       type: type of event
 * @param       arg: pointer to the event content
 * @retval      none
 */
static inline void event_post(struct event_queue *event_queue, uint8_t type, void *arg) {
        uint32_t old_tail = __sync_fetch_and_add(&(event_queue->tail), 1);
        struct event *event = EVENT(event_queue, old_tail);
        event->arg = arg;
        event->type = type;
}

/*
 * @brief       fetch an event from the event queue
 * @param       event_queue: Pointer to an event queue, USE EV_QUEUE_ID(name)
 * @param       arg: Pointer to the pointer to the event content
 * @retval      type of fetched event if succeed, or zero
 */
static inline uint8_t event_fetch(struct event_queue *event_queue, void **arg) {
        uint8_t type = 0;
        if (event_queue->head >= event_queue->tail) {
                return type;
        }

        struct event *event = EVENT(event_queue, event_queue->head);
        type = event->type;

        if (type) {
                *arg = event->arg;
                event->type = 0;
                event_queue->head++;
        }

        return type;
}

/*
 * @brief       reset an event queue
 * @param       p_eq: Pointer to an event queue, USE EV_QUEUE_ID(name)
 * @retval      none
 */
static inline void eq_reset(struct event_queue *event_queue) {
        int i;
        for (i = 0; i < EVQ_SIZE; i++) {
                event_queue->event_buf[i].type = 0;
        }
        event_queue->tail = event_queue->head = 0;
}

#endif /* _EVENT_QUEUE_H_ */
